#include <iostream>
#include "crossbit/MemoryManager.h"
#include "crossbit/SYSCALL.h"

using namespace crossbit;

MemoryManager* MemoryManager::instance = NULL;

MemoryManager::MemoryManager() : pool((XTMemAddr)NULL), size(0)
{
    ele_curr_ubound = -1;
    for(XTUint32 i = 0; i < ELEMENT_NUM; i++)
    {
        ele_pool[i] = (XTMemAddr)NULL;
        ele_size[i] = 0;
    }
    
    void *buf_exe = SYSCALL::XTMmap((void*)POOL_START_EXE, POOLSIZE_EXE, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, 0, 0 );

    if( buf_exe != MAP_FAILED )
    {
        pool_exe = (XTMemAddr)buf_exe;
        size_exe = POOLSIZE_EXE;
    }
    else 
    { 
        UTIL::XTLOG("MMAP error in MemoryManager!\n");
        UTIL::XTExit(-1);
    } 
    
    UTIL::XTMemset( (void*)pool_exe, 0, size_exe ); 
    
    void *buf = SYSCALL::XTMmap((void*)POOL_START, POOLSIZE, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, 0, 0 );
    if( buf != MAP_FAILED )
    {
        pool = (XTMemAddr)buf;
        size = POOLSIZE;
    }
    else
    {
        UTIL::XTLOG("MMAP error in MemoryManager!\n");
        UTIL::XTExit(-1);
    }
}

MemoryManager::~MemoryManager()
{
    //empty now
}

MemoryManager* MemoryManager::getInstance()
{
    if( instance == NULL )
    {
        instance = new MemoryManager();
    } 
    return instance;
}

XTMemAddr MemoryManager::allocateExecutable( XTUint32 size_req )
{
    XTMemAddr exe_pool_addr;

    //align by 16 bytes
    size_req = (size_req + 15) & 0xFFFFFFF0;

    if( size_req <= size_exe )
    {
        exe_pool_addr = (XTMemAddr)pool_exe;
        UTIL::XTMemset( (void*)pool_exe, 0, size_req );
        pool_exe += size_req;
        size_exe -= size_req;
    }
    else
    {
        //it's temporarily now
        ////allocate new pool here
        UTIL::XTLOG("Memory for exe is not enough!\n");
        exit(0);
    }

    return exe_pool_addr;
}

XTMemAddr MemoryManager::allocateObjectPool(XTUint32 * pool_id, XTUint32 pool_size, XTUint32 obj_size )
{ 
    XTMemAddr obj_addr;
    //align by 16 bytes
    pool_size = (pool_size + 15) & 0xFFFFFFF0;

    if( pool_size <= size )
    {
        ele_curr_ubound++;
        if(ele_curr_ubound > 15){ //pool id is 15 at most
            UTIL::XTLOG("Pool id is not enough.\n");
            exit(0);
        }
        *pool_id = ele_curr_ubound;
        ele_pool[*pool_id] = (XTMemAddr)pool;
        ele_size[*pool_id] = pool_size;
        UTIL::XTMemset( (void*)pool, 0, pool_size );
        pool += pool_size;
        size -= pool_size;
    }
    else
    {
        //it's temporarily now
        //allocate new pool here
        UTIL::XTLOG("Memory is not enough.\n");
        exit(0);
    }
    obj_addr = allocateObject(*pool_id, obj_size);

    return obj_addr;
}

XTMemAddr MemoryManager::allocateObject(XTUint32 pool_id, XTUint32 size_req)
{
    if(pool_id > ele_curr_ubound)
    {
        UTIL::XTLOG("Pool id:%u doesn't exist.\n", pool_id);
        exit(0);
    } 
    
    XTMemAddr buf; 
    //align by 16 bytes
    size_req = (size_req + 15) & 0xFFFFFFF0;

    if( size_req <= ele_size[pool_id] )
    {
        buf = ele_pool[pool_id];
        ele_pool[pool_id] += size_req;
        ele_size[pool_id] -= size_req;
    }
    else
    {
        //it's temporarily now
        //allocate new pool here
        UTIL::XTLOG("Memory of pool %u is not enough.\n", pool_id);
        exit(0);
    }

    return buf;
}
